Ambiten – Veröffentlichung

Ambiten Core v1.2.4 – Verbesserungen bei der Kontextbindung und der Laufzeitauflösung

Ambiten Core v1.2.4 schärft die Abgrenzungen zwischen Ausführungskontext, Modelloperationen, Datenbankanbietern, Mandanteninfrastruktur, Client-Auflösung und Transaktionsbeteiligung.

Ambiten Core v1.2.4 ist eine Version zur Verfeinerung der Laufzeitumgebung, deren Schwerpunkt darauf liegt, das Ausführungsverhalten expliziter zu gestalten. Sie stärkt die Beziehungen zwischen AmbitenContext, ModelContext, AmbitenModel, AmbitenSchema, AmbitenClient, der Mandanteninfrastruktur und Transaktionen, während das anwendungsorientierte Programmiermodell zugänglich bleibt.

Bei der Arbeit an dieser Version geht es nicht darum, die Komplexität um ihrer eigenen willen zu erhöhen. Vielmehr soll die bestehende Laufzeitumgebung leichter nachvollziehbar gemacht werden, während Ambiten den Übergang von der direkten Nutzung von MongoDB hin zu fortgeschritteneren mandantenfähigen, transaktionsorientierten Systemen vollzieht.

Ein zentraler Bestandteil dieser Arbeit bestand darin, zu klären, welche Ebene für welche Aufgabe zuständig ist.

Der Kontext bestimmt die Ausführung. Modelle verknüpfen die Ausführung mit Operationen. Die Infrastruktur legt fest, wo diese Operationen ausgeführt werden.

Ein übersichtlicheres Laufzeitmodell

Eine der wichtigsten Verbesserungen in Version 1.2.4 ist eine klarere Unterscheidung zwischen dem vollständigen Ausführungszustand und dem Modellbetriebszustand.

AmbitenContext enthält den mit einer aktiven Ausführung verbundenen Status. Dazu können die Mandantenidentität, die Anforderungsidentität, der Datenbankbereich, Transaktionssitzungen, Protokollierungsmetadaten, der Instrumentierungsstatus sowie Laufzeitsteuerelemente gehören.

AmbitenContextState
        ↓
AmbitenContext
        ↓
Application Logic
        ↓
AmbitenModel
        ↓
Effective ModelContext
        ↓
Schema / Middleware
        ↓
Infrastructure Resolution
        ↓
AmbitenClient
        ↓
MongoDB

ModelContext dient einem anderen Zweck. Es repräsentiert den für die Persistenz relevanten Zustand, der für eine bestimmte Modelloperation erforderlich ist.

Anstatt jedes Schema, jede Middleware-Funktion oder jeden Datenbankanbieter dazu aufzufordern, den aktiven Laufzeitkontext eigenständig neu aufzubauen, erstellt AmbitenModel einen einzigen effektiven Ausführungskontext.


Von „AmbitenContext“ zu „ModelContext“

Während der Modellausführung ermittelt Ambiten den effektiven ModelContext unter Verwendung einer deterministischen Prioritätsregel.

explicit operation context
        ↓
active AmbitenContext
        ↓
model defaults
        ↓
Effective ModelContext

Das bedeutet, dass der Ausführungsstatus einmalig an der Laufzeitgrenze ermittelt und anschließend nahtlos in die Persistenzvorgänge übernommen werden kann.

Ein Modellvorgang wie beispielsweise:

await UserModel.find({});

kann an einer mandanten- oder transaktionsorientierten Ausführung teilnehmen, ohne dass jeder Dienst in der Anwendung manuell Mandantenkennungen, Anforderungskennungen, Datenbanknamen oder MongoDB-Sitzungen weiterleiten muss.

Der Unterschied ist einfach:

AmbitenContextState
= full execution-scoped state

ModelContext
= model-operation-facing state

Es handelt sich nicht um konkurrierende Kontextsysteme. „ModelContext“ ist die auf die Persistenz ausgerichtete Projektion des Ausführungszustands, die von der Modell-Laufzeitumgebung benötigt wird.


AmbitenClient bleibt eine erstklassige Direkt-API

AmbitenClient hat zudem eine klarere Positionierung innerhalb der Laufzeitarchitektur erhalten.

Man sollte es nicht lediglich als eine interne Komponente betrachten, die unterhalb von AmbitenModel verborgen ist.

Die direkte Verwendung ist weiterhin beabsichtigt:

Application
    ↓
AmbitenClient
    ↓
MongoDB

Dadurch eignet sich Ambiten für kleine Anwendungen, Skripte, Lehrbeispiele, Live-Coding-Sitzungen, Migrationen, interne Tools sowie direkte Datenbank-Workflows.

Wenn eine Anwendung wächst, kann derselbe Client im Rahmen der Modell-Laufzeitumgebung mitwirken:

AmbitenContext
      ↓
AmbitenModel
      ↓
Effective ModelContext
      ↓
DbProvider
      ↓
AmbitenClient
      ↓
MongoDB

Dieses schrittweise Modell ist bewusst so gewählt. Ein Entwickler muss nicht die gesamte Multi-Tenant-Laufzeitumgebung verstehen, um eine sinnvolle Datenbankoperation durchzuführen.

Fangen Sie direkt an. Fügen Sie Struktur hinzu, wenn die Anwendung dies erfordert.

Eindeutigere Datenbankauflösung

Die Auflösung der AmbitenClient-Datenbank folgt nun einer klar dokumentierten Reihenfolge.

1. Explicit ctx.db

2. Tenant-aware resolution

3. Explicit ctx.dbName

4. Mutable database override

5. Configured or default database

Dadurch bleibt der explizite Betriebsstatus maßgeblich, während gleichzeitig eine mandantenorientierte Infrastruktur und konfigurierte Standardwerte unterstützt werden.

Für eine mandantenorientierte Ausführung kann AmbitenClient den richtigen MongoDB-Client über den konfigurierten Mandanten-Resolver ermitteln.

ModelContext.tenantId
      ↓
Tenant Client Resolver
      ↓
MongoClient
      ↓
Tenant Database

Die Anwendung muss nicht wissen, wie dieser Client erfasst, registriert oder aktiviert wurde.


Die Identität des Mandanten und die Infrastruktur des Mandanten bleiben voneinander getrennt

Mit Version 1.2.4 wird eine wichtige architektonische Trennung in Ambiten weiter verstärkt.

TenantResolver
→ identifies the tenant

AmbitenContext
→ carries tenant identity

MultiTenantManager
→ owns tenant infrastructure

AmbitenClient
→ provides MongoDB capability

Das Auflösen eines Mandanten anhand eines HTTP-Headers, eines Tokens, einer Subdomain, einer Warteschlangen- Nachricht oder eines benutzerdefinierten Resolvers ist nicht dasselbe wie das Auflösen der MongoDB-Ressourcen, die zu diesem Mandanten gehören.

Durch die Trennung dieser Aufgaben kann der MultiTenantManager die Verwaltung registrierter Mandanten, die dynamische Erkennung, die verzögerte Aktivierung, die Wiederverwendung von Clients sowie den Laufzeitstatus der Mandanten unabhängig von der Framework-Anfrage handhaben.


Ausführung von Clients in einem begrenzten Bereich

AmbitenClient unterstützt weiterhin explizite Infrastrukturbereiche.

const tenantClient =
  client.withTenant("tenant-a");

const reportingClient =
  client.withDatabase("reporting");

const scopedClient =
  client.withScope({
    tenantId: "tenant-a",
    dbName: "reporting"
  });

Diese APIs erstellen bereichsbezogene Anbieteransichten über den Basis-Client, anstatt auf nicht damit in Zusammenhang stehende MongoDB-Verbindungspools zurückzugreifen.

Ein explizit angegebener Ausführungskontext ist, sofern vorhanden, weiterhin maßgebend, während der vordefinierte Geltungsbereich als Ausweichlösung für den Client mit begrenztem Geltungsbereich dient.

Der ältere, veränderbare Datenbankumschaltpfad bleibt weiterhin verfügbar über useDatabase(), doch Client-APIs mit begrenzter Gültigkeitsbereich bieten ein sichereres Modell für parallel laufende und anforderungsorientierte Anwendungen.


Der Transaktionsstatus richtet sich nach der Ausführung

Die Transaktionsabwicklung ist ein weiterer Bereich, in dem die Zuständigkeiten nun genauer dokumentiert sind.

Transaction Boundary
      ↓
ClientSession
      ↓
AmbitenContext.session
      ↓
AmbitenModel
      ↓
ModelContext.session
      ↓
Participating Operations

Die Transaktionsgrenze bestimmt den Lebenszyklus der Transaktion.

start
commit
rollback
completion

Modelle sind an der aktiven Transaktion beteiligt. Sie führen keine eigenständige Festschreibung oder Rücknahme der übergeordneten Transaktion durch.

Dadurch können Anwendungsdienste von manuellen MongoDB -Sitzungskonfigurationen verschont bleiben, während der Laufzeitumgebung dennoch eine klare atomare Grenze gesetzt wird.


Zwei Möglichkeiten, Transaktionsgrenzen festzulegen

Ambiten unterstützt sowohl explizite Workflow-Transaktionen als auch vom Adapter verwaltete, die gesamte Ausführung umfassende Transaktionen.

Ein zielgerichteter Anwendungsworkflow kann Folgendes nutzen:

await AmbitenContext.withTransaction(
  async () => {
    await UserModel.create({
      name: "Alice"
    });

    await WalletModel.create({
      balance: 0
    });
  }
);

Ein Adapter kann stattdessen eine Transaktion rund um den unterstützten Ausführungslebenszyklus einrichten:

enableTransactions: true

Es handelt sich hierbei eher um alternative Transaktionsgrenzen als um zwei Ebenen, die jede Anwendung miteinander kombinieren muss.

Es gilt weiterhin die wichtige Regel, dass die beteiligten Ambiten-Vorgänge sich die aktive Transaktionssitzung teilen. Es sollte nicht davon ausgegangen werden, dass beliebige MongoDB-Rufanrufe oder externe Dienste automatisch in diese Transaktion einbezogen werden.


Das Schema-Verhalten ist statisch, die Ausführung ist dynamisch

AmbitenSchema fügt sich zudem besser in das weiterentwickelte Laufzeitmodell ein.

Das Schema definiert die Struktur und das persistenzorientierte Verhalten:

document structure
validation
normalization
middleware
soft-delete policy
lifecycle configuration
persistence behavior

Der Laufzeitzustand greift über den für die Modelloperation erstellten effektiven ModelContext auf das Schema und das Middleware-Verhalten zu.

AmbitenContext
      ↓
AmbitenModel
      ↓
Effective ModelContext
      ↓
AmbitenSchema / Middleware

Das Schema selbst wird weder mandantenspezifisch noch anfragespezifisch.

Statische Definition. Dynamische Ausführung.

Eine Anfrage, von Anfang bis Ende

Die zugehörige Dokumentation verfolgt nun eine Anfrage durch die Laufzeitumgebung unter Verwendung derselben Zuständigkeitsgrenzen, die intern von Ambiten verwendet werden.

HTTP Request
      ↓
Adapter
      ↓
Adapter Runtime
      ↓
Tenant Resolution
      ↓
AmbitenContext
      ↓
Application Handler
      ↓
AmbitenModel
      ↓
Effective ModelContext
      ↓
Schema / Middleware
      ↓
Infrastructure Resolution
      ↓
AmbitenClient
      ↓
MongoDB
      ↓
Result
      ↓
Transaction Completion if active
      ↓
Framework Response

Das Ziel besteht nicht darin, eine einfache Anforderung kompliziert erscheinen zu lassen. Das Ziel ist es, die verborgenen Verantwortlichkeiten sichtbar zu machen, damit jede Schicht auch bei wachsendem Umfang der Anwendung verständlich bleibt.


Klarere Abgrenzung der Zuständigkeiten

Die Architektur in Version 1.2.4 lässt sich anhand einiger weniger, klar definierter Aufgabenbereiche zusammenfassen.

Adapter
→ execution ingress

TenantResolver
→ tenant identity

AmbitenContext
→ execution state

AmbitenModel
→ operation coordination and context binding

ModelContext
→ persistence-facing operation state

AmbitenSchema
→ structure and persistence behavior

DbProvider
→ database, client, and session contract

MultiTenantManager
→ tenant infrastructure

AmbitenClient
→ MongoDB capability

Transaction Boundary
→ transaction lifecycle

MongoDB
→ persistence

Es ist wichtig, diese Grenzen klar zu definieren, da Ambiten sowohl eine unkomplizierte, direkte Nutzung als auch anspruchsvollere Laufzeitarchitekturen unterstützen soll.


Wiederverwendbare Infrastruktur, isolierte Ausführung

Eine weitere wichtige Unterscheidung ist der Unterschied zwischen Ressourcen auf Prozessebene und dem Zustand auf Ausführungsebene.

PROCESS LIFETIME

AmbitenRuntime
AmbitenClient
MongoClient
MultiTenantManager
providers
runtime configuration
EXECUTION LIFETIME

AmbitenContext
tenantId
requestId
dbName
collectionName
session
logger metadata
runtime metadata

Eine MongoDB-Verbindung kann über mehrere Ausführungen hinweg wiederverwendet werden, während die Mandantenidentität, die Anforderungsidentität, der Transaktionsstatus und die Laufzeit-Metadaten auf die jeweilige Ausführung beschränkt bleiben, in der sie erstellt wurden.

Dies ist eine der Grundlagen, die es derselben Ambiten-Laufzeitumgebung ermöglicht, gleichzeitige Arbeitslasten zu bedienen, ohne dass wiederverwendbare Infrastruktur in einen anforderungsspezifischen, veränderbaren Zustand überführt wird.


Eine schrittweise Einführung ist nach wie vor wichtig

Ambiten spielt seine Stärken vor allem dann aus, wenn Anwendungen ein strukturiertes Laufzeitverhalten erfordern; das Framework wurde jedoch nicht so konzipiert, dass diese Architektur eine Voraussetzung für den Einstieg darstellt.

Ein Entwickler kann hier beginnen:

AmbitenClient
      ↓
MongoDB

Führen Sie anschließend den Kontext ein:

AmbitenContext
      ↓
AmbitenClient / Application

Anschließend die wiederverwendbare Schema- und Modellstruktur:

AmbitenSchema
      ↓
AmbitenModel

Und schließlich:

Framework Adapters
MultiTenantManager
Dynamic Tenants
Transactions
Middleware
Instrumentation
Runtime Orchestration

Anwendungen können den Umfang an Struktur nutzen, der ihren tatsächlichen Anforderungen entspricht.


Dokumentation mit der Laufzeit aktualisiert

Parallel zur Veröffentlichung der Core-Version wurden mehrere Bereiche der Dokumentation zu Ambiten überarbeitet, damit die öffentliche Architektur genauer mit der Laufzeitumgebung übereinstimmt.

Die Aktualisierungen betreffen AmbitenClient, Schema, Kontext, Kontextbindung, Transaktionen, Provider-Verantwortlichkeiten, die Weitergabe des ModelContext, die Multi-Tenant-Infrastruktur sowie den gesamten One-Request-Ablauf.

Ein besonderer Schwerpunkt lag dabei auf der Beseitigung mehrdeutiger Formulierungen in Bezug auf:

execution state
vs
operation state

tenant identity
vs
tenant infrastructure

transaction ownership
vs
transaction participation

model responsibility
vs
provider responsibility

direct client usage
vs
model-runtime usage

Eine klare Terminologie gewinnt mit zunehmender Größe einer Laufzeitumgebung immer mehr an Bedeutung. Die Architektur sollte nicht nur funktionieren. Entwickler sollten in der Lage sein, zu verstehen, warum sie funktioniert und wo die jeweiligen Zuständigkeiten liegen.


Installation von Ambiten Core v1.2.4

Die Veröffentlichung ist über die @ambiten/core Paket.

pnpm add @ambiten/core@1.2.4

oder:

npm install @ambiten/core@1.2.4

Der Weg in die Zukunft

Ambiten Core v1.2.4 ist eine Verfeinerungsversion, doch die Grenzen, die sie verdeutlicht, sind für die zukünftige Ausrichtung der Laufzeitumgebung von Bedeutung.

Der Kontext sollte eine Ausführung beschreiben, ohne selbst zu Anwendungsdaten zu werden. Modelle sollten Persistenzvorgänge koordinieren, ohne den Lebenszyklus der Infrastruktur zu steuern. Die Identität eines Mandanten sollte von dessen Infrastruktur getrennt bleiben. Transaktionen sollten die Atomizität gewährleisten, ohne die Verwendung von Sitzungsobjekten im gesamten Geschäftslogikcode zu erzwingen.

Und der direkte Zugriff auf MongoDB sollte auch dann weiterhin möglich sein, wenn die gleiche Laufzeitumgebung bereits wesentlich komplexere Systeme unterstützen kann.

Der Kontext steuert die Ausführung. Das Modell bindet die Ausführung an eine Operation. Die Infrastruktur bestimmt, wo diese ausgeführt wird. MongoDB sorgt für die Persistenz.

Dies wird durch das Laufzeitmodell Ambiten Core v1.2.4 immer deutlicher.